package br.upe.dsc.caeto.utils.xml;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import br.upe.dsc.caeto.core.InvalidElementException;
import br.upe.dsc.caeto.core.application.Application;
import br.upe.dsc.caeto.core.cacheunit.CacheUnit;
import br.upe.dsc.caeto.core.cacheunit.CacheUnitExplorer;
import br.upe.dsc.caeto.core.cacheunit.CacheUnitSimple;
import br.upe.dsc.caeto.core.hierarchy.Hierarchy;
import br.upe.dsc.caeto.core.project.Project;

import java.util.Iterator;
import java.util.List;

public class ProjectXml {
    private String separator;
	
    public ProjectXml() {
    	separator = System.getProperties().getProperty("file.separator");
    }

    public void write(Document document, String projectFile) {
    	File file = new File(projectFile);
		try {
			FileWriter writer = new FileWriter(file, false);
			XMLOutputter outputter = new XMLOutputter(org.jdom.output.Format.getPrettyFormat());
			outputter.output(document, writer);
		} catch (java.io.IOException e) {
			System.out.println("ERROR:");
			System.out.print("ProjectXml class, ");
			System.out.println("error in write method -> IOException.");
			//e.printStackTrace();
		}    	
    }
    
	public void write(Project project) {
		String projectFile = project.getPath() + separator + project.getName();
		File file = new File(projectFile);
		Document document = null;
		Element rootElement = new Element("project");
		rootElement.setAttribute("name", project.getName());
		rootElement.setAttribute("path", project.getPath());
		
		Iterable<Application> applications = project.getApplications();
		for (Application application : applications) {
			rootElement.addContent(createChildElement(application));
		}
		
		document = new Document(rootElement);
		try {
			FileWriter writer = new FileWriter(file, false);
			XMLOutputter outputter = new XMLOutputter(org.jdom.output.Format.getPrettyFormat());
			outputter.output(document, writer);
		} catch (java.io.IOException e) {
			System.out.println("ERROR:");
			System.out.print("ProjectXml class, ");
			System.out.println("error in write method -> IOException.");
			//e.printStackTrace();
		}
	}
	
	public void write(Project project, Application application) {
		String projectFile = project.getPath() + separator + project.getName();
		File file = new File(projectFile);
		Document document = null;
		if (file.exists()) {
			document = read(projectFile);
			document.getRootElement().addContent(createChildElement(application));
		}
		else {
			Element rootElement = new Element("project");
			rootElement.setAttribute("name", project.getName());
			rootElement.setAttribute("path", project.getPath());
			rootElement.addContent(createChildElement(application));
			document = new Document(rootElement);
		}
		try {
			FileWriter writer = new FileWriter(file, true);
			XMLOutputter outputter = new XMLOutputter(org.jdom.output.Format.getPrettyFormat());
			outputter.output(document, writer);
		} catch (java.io.IOException e) {
			System.out.println("ERROR:");
			System.out.print("ProjectXml class, ");
			System.out.println("error in write method -> IOException.");
			//e.printStackTrace();
		}
	}

	public Document read(String projectFile) {
		Document document = null;
		File file = new File(projectFile);

		if (file.exists()) {
			SAXBuilder builder = new SAXBuilder();

			try {
				document = builder.build(file);
			}
			catch (java.io.FileNotFoundException e) {
				e.printStackTrace();
			}
			catch (java.io.IOException e) {
				e.printStackTrace();
			}
			catch (org.jdom.JDOMException e) {
				e.printStackTrace();
			}
		}

		return document;
	}

	public Element createChildElement(Application application) {
		Element applicationElement = new Element("application");		
		Element benchmarkElement = new Element("benchmark");
		Element inputFileElement = new Element("inputFile");
		Element outputFileElement = new Element("outputFile");
		Element hierarchyElement = new Element("hierarchy");
		Element tensaoAlimentacaoElement = new Element("tensaoAlimentacao");
		Element transistorTechnologyElement = new Element("transistorTechnology");

		applicationElement.setAttribute("name", application.getName());
		benchmarkElement.addContent(application.getBenchMark());
		inputFileElement.addContent(application.getArquivoEntrada());
		outputFileElement.addContent(application.getArquivoSaida());

		hierarchyElement.setAttribute("name", application.getHierarquia().getName());
		hierarchyElement.addContent(tensaoAlimentacaoElement);
		hierarchyElement.addContent(transistorTechnologyElement);

		Iterable<CacheUnit> iterator = application.getHierarquia().getCacheUnits();
		CacheUnitSimple simple;
		CacheUnitExplorer explorer;
		
		for (CacheUnit tmp : iterator) {
			if (tmp instanceof CacheUnitSimple) {
				Element tagCacheUnit = new Element("cacheUnit");
				Element tagCacheSize = new Element("cacheSize");
				Element tagLineSize = new Element("lineSize");
				Element tagAssociativity = new Element("associativity");
				tagCacheUnit.setAttribute("type", "CacheUnitSimple");

				simple = (CacheUnitSimple) tmp;

				tagCacheUnit.setAttribute("name", simple.getName());
				tagCacheSize.addContent(String.valueOf(simple.getTamanho()));
				tagLineSize.addContent(String.valueOf(simple.getTamLinha()));
				tagAssociativity.addContent(String.valueOf(simple.getAssociatividade()));

				tagCacheUnit.addContent(tagCacheSize);
				tagCacheUnit.addContent(tagLineSize);
				tagCacheUnit.addContent(tagAssociativity);

				hierarchyElement.addContent(tagCacheUnit);				
			} else {
				Element tagCacheUnit = new Element("cacheUnit");
				Element tagCacheSizeMinimum = new Element("cacheSizeMinimum");
				Element tagCacheSizeMaximum = new Element("cacheSizeMaximum");
				Element tagLineSizeMinimum = new Element("lineSizeMinimum");
				Element tagLineSizeMaximum = new Element("lineSizeMaximum");
				Element tagAssociativityMinimum = new Element("associativityMinimum");
				Element tagAssociativityMaximum = new Element("associativityMaximum");
				tagCacheUnit.setAttribute("type", "CacheUnitExplorer");

				explorer = (CacheUnitExplorer) tmp;

				tagCacheUnit.setAttribute("name", explorer.getName());
				tagCacheSizeMinimum.addContent(String.valueOf(explorer.getTamanho()[0]));
				tagCacheSizeMaximum.addContent(String.valueOf(explorer.getTamanho()[1]));
				tagLineSizeMinimum.addContent(String.valueOf(explorer.getTamLinha()[0]));
				tagLineSizeMaximum.addContent(String.valueOf(explorer.getTamLinha()[1]));
				tagAssociativityMinimum.addContent(String.valueOf(explorer.getAssociatividade()[0]));
				tagAssociativityMaximum.addContent(String.valueOf(explorer.getAssociatividade()[1]));

				tagCacheUnit.addContent(tagCacheSizeMinimum);
				tagCacheUnit.addContent(tagCacheSizeMaximum);
				tagCacheUnit.addContent(tagLineSizeMinimum);
				tagCacheUnit.addContent(tagLineSizeMaximum);
				tagCacheUnit.addContent(tagAssociativityMinimum);
				tagCacheUnit.addContent(tagAssociativityMaximum);

				hierarchyElement.addContent(tagCacheUnit);
			}
		}
		
		applicationElement.addContent(benchmarkElement);
		applicationElement.addContent(inputFileElement);
		applicationElement.addContent(outputFileElement);
		applicationElement.addContent(hierarchyElement);

		return applicationElement;
	}
	
    public Project getProject(Document document) {
    	Element rootElement = document.getRootElement();
    	Project project = new Project(rootElement.getAttributeValue("name"),
    			rootElement.getAttributeValue("path"));
    	List<Element> applications = rootElement.getChildren();

    	Application app;
    	
    	try {
	    	for (Element application : applications) {
	    		app = buildApplicationObject(
	    				rootElement.getAttributeValue("name"), application);
	    		
	    		project.getApplications().add(app);
	    	}
    	} catch(InvalidElementException e) {
    		System.out.println(e.getMessage());
    	}
    	
    	return project;
    }
	
    private Application buildApplicationObject(String projectName, Element applicationElement) {
    	Application application = null;
    	
		String benchmark = applicationElement.getChildText("benchmark");
		String inputFile = applicationElement.getChildText("inputFile");
		String outputFile = applicationElement.getChildText("outputFile");
		String hierarchyName = applicationElement.getChild("hierarchy").getAttributeValue("name");
		String tensaoAlimentacao = "";//application.getChildText("tensaoAlimentacao");
		String transistorTechnology = "";//application.getChildText("transistorTechnology");
    	
		Hierarchy hierarchy = new Hierarchy(hierarchyName, tensaoAlimentacao, transistorTechnology);
		
		List<Element> cacheUnits = applicationElement.getChild("hierarchy").getChildren("cacheUnit");
		CacheUnitExplorer cacheSet;
		CacheUnitSimple cacheSimple;
		int[] associativity = new int[2];
		int[] lineSize = new int[2];
		int[] size = new int[2];
		
		for (Element cache : cacheUnits) {
			if (cache.getAttributeValue("type").equals("CacheUnitSimple")) {
				associativity[0] = Integer.parseInt(cache.getChildText("associativity"));
				lineSize[0] = Integer.parseInt(cache.getChildText("lineSize"));
				size[0] = Integer.parseInt(cache.getChildText("cacheSize"));
				
				cacheSimple = new CacheUnitSimple(cache.getAttributeValue("name"),
						          associativity[0], lineSize[0], size[0]);
				try {
					hierarchy.addCacheUnit(cacheSimple);
				}
				catch (InvalidElementException e) {
					System.out.println(e.getMessage());
				}
			}
			else {
				associativity = new int[2];
				lineSize = new int[2];
				size = new int[2];
				
				associativity[0] = Integer.parseInt(cache.getChildText("associativityMinimum"));
				associativity[1] = Integer.parseInt(cache.getChildText("associativityMaximum"));
				lineSize[0] = Integer.parseInt(cache.getChildText("lineSizeMinimum"));
				lineSize[1] = Integer.parseInt(cache.getChildText("lineSizeMaximum"));
				size[0] = Integer.parseInt(cache.getChildText("cacheSizeMinimum"));
				size[1] = Integer.parseInt(cache.getChildText("cacheSizeMaximum"));
				
				cacheSet = new CacheUnitExplorer(cache.getAttributeValue("name"),
						          associativity, lineSize, size);

				try {
					hierarchy.addCacheUnit(cacheSet);
				}
				catch (InvalidElementException e) {
					System.out.println(e.getMessage());
				}
			}
		}
		
		application = new Application(applicationElement.getAttributeValue("name"),
								projectName,
				                benchmark, "", inputFile, outputFile, hierarchy);
		
    	return application;
    }
}
